/*
	       File: FPServiceInstaller.cs
	Create Date: 2007-09-18
	    Version:

	Description:


	 Change log:


*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using GuerillaProgrammer.FileProcessor;
using GuerillaProgrammer.FileProcessor.Settings;

using Microsoft.Win32.Security;

namespace FPInstaller
{
    public class FPServiceInstaller
    {
        #region Private member fields        
        [DllImport("advapi32.dll")]
        private static extern IntPtr OpenSCManager(string lpMachineName, string lpSCDB, int scParameter);
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, uint dwDesiredAccess, uint dwServiceType, uint dwStartType, uint dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, uint lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseServiceHandle(IntPtr hSCObject);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool DeleteService(IntPtr hService);

        private const int SC_MANAGER_CREATE_SERVICE = 0x0002;
        private const int STANDARD_RIGHTS_REQUIRED = (0x000F0000);
        private const int  SERVICE_QUERY_CONFIG = 0x0001;
        private const int  SERVICE_CHANGE_CONFIG = 0x0002;
        private const int  SERVICE_QUERY_STATUS = 0x0004;
        private const int  SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
        private const int  SERVICE_START = 0x0010;
        private const int  SERVICE_STOP = 0x0020;
        private const int  SERVICE_PAUSE_CONTINUE = 0x0040;
        private const int  SERVICE_INTERROGATE = 0x0080;
        private const int SERVICE_USER_DEFINED_CONTROL = 0x0100;
        private const int SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL);
        private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
        private const int SERVICE_AUTO_START = 0x00000002;
        private const int SERVICE_ERROR_NORMAL = 0x00000001;
        #endregion

        #region Private implementation
        private static void WriteServiceAppConfig(string applicationFolder)
        {
            //do I need to update appconfig?
            string appConfigPath = Path.Combine(applicationFolder, "FileProcessorService.exe.config");
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(appConfigPath);

            XmlNode node = xmlDoc.SelectSingleNode("/configuration/loggingConfiguration/listeners/add");
            if (null != node)
            {
                node.Attributes["fileName"].Value = Path.Combine(applicationFolder, "Logs\\FileProcService.log");
                xmlDoc.Save(appConfigPath);
            }
        }
        private static void SetDirectoryACLS(string path, string userID)
        {
            SecurityDescriptor secDesc = SecurityDescriptor.GetFileSecurity(path, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);
            Dacl dacl = secDesc.Dacl;

            Sid sidUser = new Sid("Everyone");

            dacl.AddAce(new AceAccessAllowed(sidUser, AccessType.GENERIC_WRITE
                | AccessType.GENERIC_READ
                | AccessType.DELETE
                | AccessType.GENERIC_EXECUTE
                , AceFlags.OBJECT_INHERIT_ACE | AceFlags.CONTAINER_INHERIT_ACE));

            secDesc.SetDacl(dacl);
            secDesc.SetFileSecurity(path, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);
        }
        private static IntPtr GetSCM()
        {
            IntPtr scm = IntPtr.Zero;
            IntPtr newServiceHandle = IntPtr.Zero;
            //uint groupID = 0;
            scm = OpenSCManager(null, null, SC_MANAGER_CREATE_SERVICE);
            if (IntPtr.Zero == scm)
            {
                //we failed!
                int error = Marshal.GetLastWin32Error();
                MessageBox.Show(string.Format("Error getting SCM!\nError: {0}", error), "Error getting SCM!");
                return IntPtr.Zero;
            }

            return scm;
        }
        private static void UninstallService()
        {
            IntPtr scm = GetSCM();
            if (IntPtr.Zero == scm)
            {
                return;
            }

            IntPtr schService = OpenService(scm, "Grinder", SERVICE_ALL_ACCESS);
            if (IntPtr.Zero != schService)
            {
                if (true == DeleteService(schService))
                {
                    MessageBox.Show("Service has been deleted", "Delete Service");
                }
                else
                {
                    MessageBox.Show(string.Format("Could not delete service!\nError: {0}", Marshal.GetLastWin32Error()), "Service not deleted");
                }
                CloseServiceHandle(schService);
            }
            CloseServiceHandle(scm);
        }
        #endregion

        #region Public interface
        public static bool InstallService(string username, string password, string applicationPath)
        {
            IntPtr scm = IntPtr.Zero;
            IntPtr newServiceHandle = IntPtr.Zero;
            uint groupID = 0;
            scm = OpenSCManager(null, null, SC_MANAGER_CREATE_SERVICE);
            if (IntPtr.Zero == scm)
            {
                //we failed!
                int error = Marshal.GetLastWin32Error();
                return false;
            }

            newServiceHandle = CreateService(scm, "Grinder", "Grinder", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, applicationPath, null, groupID, null, username, password);
            if (IntPtr.Zero == newServiceHandle)
            {
                int error = Marshal.GetLastWin32Error();
                return false;
            }

            return true;
        }
        public static void WriteConfiguration(string applicationFolder, string archivePath, string assemblyPath)
        {
            //create configuration file
            Configuration appConfig = new Configuration();
            appConfig.Services = new ConfigurationServices();
            appConfig.Directories = new List<ConfigurationDirectory>();
            appConfig.FileTypes = new List<ConfigurationFileType>();
            appConfig.Workflows = new List<ConfigurationWorkflow>();

            appConfig.Services.ArchiveDirectory = archivePath;
            appConfig.Services.AssemblyDirectory = assemblyPath;
            appConfig.Services.WorkingDirectory = Path.Combine(applicationFolder, "WorkingDirectory");
            appConfig.Services.MaxDirectoryWorkerThreads = "2";
            appConfig.Services.MaxFileProcessorWorkThreads = "2";
            appConfig.Services.PollingTime = "300"; //set to 5 minutes

            string configurationName = Path.Combine(applicationFolder, "configuration.xml");

            ConfigurationManager.SaveServiceConfiguration(appConfig, configurationName);

            WriteServiceAppConfig(applicationFolder);
        }
        public static void WriteDesignerConfiguration(string applicationFolder, string assemblyPath)
        {
            string appConfigFileName = Path.Combine(applicationFolder, "WorkflowDesigner.exe.config");
            string appDesignerConfig = Path.Combine(applicationFolder, "WFDConfiguration.xml");

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(appConfigFileName);

            XmlNode nodeConfig = xmlDoc.SelectSingleNode("/configuration/appSettings/add");
            if (null != nodeConfig)
            {
                string xomlDirectory = Path.Combine(applicationFolder, "XOML");
                WorkflowDesignerConfiguration designerConfig = new WorkflowDesignerConfiguration();
                designerConfig.AssemblyOutputDirectory = assemblyPath;
                designerConfig.WorkflowOutputDirectory = xomlDirectory;
                designerConfig.Libraries = new List<string>();
                designerConfig.Libraries.Add(assemblyPath);
                designerConfig.Libraries.Add(applicationFolder);

                designerConfig.ReferencedAssemblies = new List<string>();
                designerConfig.ReferencedAssemblies.Add("FileProcessorActivities.dll");

                designerConfig.DesignerActivities = new List<WorkflowDesignerConfigurationActivity>();
                WorkflowDesignerConfigurationActivity copyActivity = new WorkflowDesignerConfigurationActivity();
                WorkflowDesignerConfigurationActivity deleteActivity = new WorkflowDesignerConfigurationActivity();
                WorkflowDesignerConfigurationActivity moveActivity = new WorkflowDesignerConfigurationActivity();
                WorkflowDesignerConfigurationActivity executeActivity = new WorkflowDesignerConfigurationActivity();
                WorkflowDesignerConfigurationActivity unzipActivity = new WorkflowDesignerConfigurationActivity();
                WorkflowDesignerConfigurationActivity zipActivity = new WorkflowDesignerConfigurationActivity();

                copyActivity.Type = "FileCopy,FileProcessorActivities";
                deleteActivity.Type = "FileDelete,FileProcessorActivities";
                moveActivity.Type = "FileMove,FileProcessorActivities";
                executeActivity.Type = "ExecuteApplication,FileProcessorActivities";
                unzipActivity.Type = "UnzipFile,FileProcessorActivities";
                zipActivity.Type = "ZipFile,FileProcessorActivities";
                designerConfig.DesignerActivities.Add(copyActivity);
                designerConfig.DesignerActivities.Add(deleteActivity);
                designerConfig.DesignerActivities.Add(moveActivity);
                designerConfig.DesignerActivities.Add(executeActivity);
                designerConfig.DesignerActivities.Add(unzipActivity);
                designerConfig.DesignerActivities.Add(zipActivity);

                ConfigurationManager.SaveWFDesignerConfiguration(designerConfig, appDesignerConfig);

                nodeConfig.Attributes["value"].Value = "WFDConfiguration.xml";
                xmlDoc.Save(appConfigFileName);
            }
        }
        public static void WriteEditorConfiguration(string applicationFolder)
        {
            string appConfigFileName = Path.Combine(applicationFolder, "FPConfigurationEditor.exe.config");
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(appConfigFileName);

            XmlNode nodeConfig = xmlDoc.SelectSingleNode("/configuration/appSettings/add");
            if (null != nodeConfig)
            {
                nodeConfig.Attributes["value"].Value = Path.Combine(applicationFolder, "WorkflowDesigner.exe");
                xmlDoc.Save(appConfigFileName);
            }
        }
        public static void DoInstall(string[] args)
        {
            string appFolder;
            //System.Diagnostics.Debugger.Break();
            if (1 == args.Length)
            {
                switch (args[0])
                {
                    case "TEST":
                        {
                            Application.EnableVisualStyles();
                            Application.SetCompatibleTextRenderingDefault(false);
                            Application.Run(new frmMain());
                            return;
                        }
                    case "UNINSTALL":
                        {
                            appFolder = AppDomain.CurrentDomain.BaseDirectory;
                            UninstallService();
                            return;
                        }
                    default:
                        {
                            string[] pieces = args[0].Split(new char[] { '=' });
                            appFolder = pieces[1].Replace("\"","");
                            break;
                        }
                }
            }
            else
            {
                appFolder = AppDomain.CurrentDomain.BaseDirectory;
            }
            //Get user ID
            frmUserID userID = new frmUserID();
            if (DialogResult.OK == userID.ShowDialog())
            {
                //I need to get the path to the application

                string app = Path.Combine(appFolder, "FileProcessorService.exe");

                //setup service
                if (false == FPServiceInstaller.InstallService(userID.UserName, userID.Password, app))
                {
                    //We don't really care but the user should be told
                    MessageBox.Show("The service failed to install.\nYou can manually install it instead.\nSetup will proceed.");

                }

                //select archive directory
                frmArchiveDirectory frmArchDir = new frmArchiveDirectory();
                string archiveFolder = "";
                if (DialogResult.OK == frmArchDir.ShowDialog())
                {
                    archiveFolder = frmArchDir.ArchiveDirectory;
                    if (false == Directory.Exists(archiveFolder))
                    {
                        Directory.CreateDirectory(archiveFolder);
                    }
                    SetDirectoryACLS(archiveFolder, userID.UserName);
                }

                SetDirectoryACLS(Path.Combine(appFolder, "WorkingDirectory"), userID.UserName);
                SetDirectoryACLS(Path.Combine(appFolder, "Logs"), userID.UserName);
                string assemblyFolder = Path.Combine(appFolder, "Assemblies");
                FPServiceInstaller.WriteConfiguration(appFolder, archiveFolder, assemblyFolder);
                FPServiceInstaller.WriteDesignerConfiguration(appFolder, assemblyFolder);
                FPServiceInstaller.WriteEditorConfiguration(appFolder);
            }
        }
        #endregion
    }
}
